// f4f.c
#include "f4f.h"

// deprecated
void print_hex(unsigned char* buffer, int size)
{
	int i=0;
	for (i=0; i < size; i++)
	{
		printf("%02X ", buffer[i]);
	}
	printf("\t");
}

void print_ascii(unsigned char* buffer, int size)
{
	int i=0;
	for (i=0; i < size; i++)
	{
		if (buffer[i] >='a' && buffer[i] <= 'z')
			printf("%c ", (char)buffer[i]);
		else if (buffer[i] >='A' && buffer[i] <= 'Z')
			printf("%c ", (char)buffer[i]);
		else if (buffer[i] >='0' && buffer[i] <= '9')
			printf("%c ", (char)buffer[i]);
		else
			printf("_ ");
	}
	printf("\t");
}

PF4F_FORMAT	f4f_create()
{
	PF4F_FORMAT p = 0;

	p = (PF4F_FORMAT) malloc(sizeof(F4F_FORMAT));
	if (p)
	{
		memset(p, 0, sizeof(F4F_FORMAT));
	}
	return p;
}

void		f4f_close(PF4F_FORMAT p)
{
	if (p)
	{
		if (p->header)
		{
			if (p->header->data)
				free(p->header->data);
			free(p->header);
		}
		if (p->truns)
		{
			free(p->truns);
		}
		if (p->filename)
			free(p->filename);
		if (p->pb)
			fclose(p->pb);
		free(p);
	}
}

int 		f4f_read_moof(PF4F_FORMAT p)
{
	if (p && p->pb)
	{
		uint32_t	size;
		if (!p->header)
			p->header = (PF4F_HEADER) malloc(sizeof(F4F_HEADER));
		if (!p->header)
			return 0;

		// read header size
		fread(&size, sizeof(uint32_t), 1, p->pb);
		p->header->size = htonl(size);

#if (F4F_DEBUG == 1)
		printf("%s : header size : %d\r\n", p->filename, p->header->size);
#endif
		// read header type
		fread(&p->header->type, sizeof(uint32_t), 1, p->pb);
		if (p->header->type == MKTAG('m','o','o','f'))
		{
			// alloc header
			p->header->data = (BYTE*) malloc(p->header->size);
			// read header all
			if (p->header->data)
			{
				fread(p->header->data, p->header->size - 8, sizeof(BYTE), p->pb);
				return 1;
			}
		}		
	}
	return 0;
}

int 		f4f_read_header(PF4F_FORMAT p, uint64_t start_time)
{
	if (p && p->pb && p->header && p->header->data)
	{
		BYTE			*pos = 0, *start = 0;
		uint32_t		size, type, readsize, rest, index;
		uint64_t		pts = start_time;

		// ser start position
		start = pos = p->header->data;
		while (pos - start < p->header->size - 8)
		{
			// seek next pos
			size = htonl(*((uint32_t*)pos)) - 8;
			// get type
			pos += 4;
			type = *((uint32_t*)pos);
			pos += 4;

			switch (type)
			{
				case MKTAG('u','u','i','d'):	// not parse at this time
				case MKTAG('s','d','t','p'):	// not parse at this time
				case MKTAG('m','f','h','d'):	// not parse at this time
				case MKTAG('t','f','h','d'):	// not parse at this time
				case MKTAG('m','d','a','t'):	// not parse at this time
#if (F4F_DEBUG == 1)
					printf("0x%04X : ", type);
					print_hex(pos, size);
					printf("\r\n");
#endif
					pos += size;
					break;
				case MKTAG('t','r','a','f'):	// start pos of track header
					break;
				case MKTAG('t','r','u','n'):
					readsize = 0;
					// get version
					p->version = (uint32_t)pos[0];
					pos += 1;
					readsize += 1;
					// get falgs
					p->flags = GET_BE24(pos[0], pos[1], pos[2]);
					pos += 3;
					readsize += 3;
					// get entries
					p->entries = GET_BE32(pos[0], pos[1], pos[2], pos[3]);					
					pos += 4;
					readsize += 4;
					index = 0;
					if (!p->truns && p->entries > 0)
					{
						p->truns = (PF4F_TRUN) malloc(sizeof(F4F_TRUN) * p->entries);
						memset(p->truns, 0, sizeof(F4F_TRUN) * p->entries);
					}
					while (readsize < size)
					{
						int sample_duration=0, sample_size=0, sample_flags=0, duration=0;
						if (p->flags & 0x100) {sample_duration = GET_BE32(pos[0], pos[1], pos[2], pos[3]); pos += 4; readsize += 4;}
				        if (p->flags & 0x200) {sample_size     = GET_BE32(pos[0], pos[1], pos[2], pos[3]); pos += 4; readsize += 4;}
				        if (p->flags & 0x400) {sample_flags    = GET_BE32(pos[0], pos[1], pos[2], pos[3]); pos += 4; readsize += 4;}
				        if (p->flags & 0x800) {duration 	   = GET_BE32(pos[0], pos[1], pos[2], pos[3]); pos += 4; readsize += 4;}
#if (F4F_DEBUG == 1)
				        printf("%08d: sample_duration(%08d), sample_size(%08d), "
				        	   "sample_flags(%08d), duration=(%08d), pts(%012lld)\r\n", 
				        	   index, sample_duration, sample_size, sample_flags, duration, pts);
#endif
				        if (p->truns)
				        {
				        	p->truns[index].sample_duration = sample_duration;
				        	p->truns[index].sample_size 	= sample_size;
				        	p->truns[index].sample_flags	= sample_flags;
				        	p->truns[index].duration 		= duration;
				        	p->truns[index].pts 			= pts;
				        	pts += (sample_duration / 10);
				        	index++;
				        }						
					}
					break;
				default:
					pos += size;
					break;
			}
		}
		return 1;
	}
	return 0;
}

PF4F_FORMAT	f4f_open_file(const char* filename, uint64_t start_time)
{
	PF4F_FORMAT p = f4f_create();
	if (p)
	{
		p->pb = fopen(filename, "rb");

		// store file name
		if (!p->filename)
		{
			p->filename = (char*) malloc(strlen(filename) + 1);
			if (!p->filename)
				goto error_exit;
			strcpy(p->filename, filename);
		}
		if (!p->pb)
			goto error_exit;

		if (!f4f_read_moof(p))
			goto error_exit;

		if (!f4f_read_header(p, start_time))
			goto error_exit;

		return p;
	}
error_exit:
	if (p)
	{
		f4f_close(p);
		p = 0;
	}
	return p;
}

int 		f4f_read_mdat(PF4F_FORMAT p)
{
	int ret = 0;
	BYTE buffer[8];
	ret = f4f_read_frame(p, buffer, F4F_MDAT_LEN);
#if (F4F_DEBUG == 1)
	printf("mdat : ");
	print_hex(buffer, ret);
	printf("\r\n");	
#endif
	return ret;
}

int 		f4f_read_frame(PF4F_FORMAT p, char* buffer, int read_bytes)
{
	if (p && p->pb) 
	{
		return fread(buffer, 1, read_bytes, p->pb);
	}
	return 0;
}

int64_t		f4f_seek_pos(PF4F_FORMAT p, int64_t pos)
{
	if (p && p->pb)
	{
		fseek(p->pb, pos, SEEK_SET);
		return ftell(p->pb);
	}
	return 0;
}

int 		f4f_get_next_frame(PF4F_FORMAT p, PF4F_PACKET pkt)
{
	int ret = 0;
	if (p && p->pb && p->truns)
	{
		f4f_release_packet(pkt);
#if (F4F_DEBUG == 1)
		printf("f4f_get_next_frame(%d)\r\n", p->frame_index);
#endif
		if (p->frame_index < p->entries && p->truns[p->frame_index].sample_size > 0)
		{
			pkt->data = (BYTE*) malloc(p->truns[p->frame_index].sample_size);
			ret = f4f_read_frame(p, pkt->data, p->truns[p->frame_index].sample_size);
#if (F4F_DEBUG == 1)
			printf("%d->ss(%d),ret(%d)\r\n", p->frame_index, p->truns[p->frame_index].sample_size, ret);
#endif
			if (ret > 0)
			{
				pkt->pts = p->total_duration + p->truns[p->frame_index].sample_duration + p->truns[p->frame_index].duration;
				pkt->dts = p->truns[p->frame_index].sample_duration + p->truns[p->frame_index].duration;;
				p->total_duration += p->truns[p->frame_index].sample_duration;
				pkt->size = p->truns[p->frame_index].sample_size;
				p->frame_index++;
				return ret;
			}
		}
		else
			return -1;
	}
	return 0;
}

int32_t		f4f_init_packet(PF4F_PACKET p)
{
	if (p)
	{
		p->pts = 0;
		p->dts = 0;
		p->data = 0;
		p->size= 0;
		return 1;
	}
	return 0;
}

int32_t		f4f_release_packet(PF4F_PACKET p)
{
	if (p)
	{
		if (p->data)
			free(p->data);
		f4f_init_packet(p);
	}
	return 0;
}